﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Cofe.Core.Dynamic;
using Cofe.Core.EntryType;
using Cofe.Core.Property;
using Cofe.Web;

namespace Cofe.Core.Implements
{
#if UseDataContractToSerialize
    [DataContract(Name = "Entry", Namespace = CofeConstants.Namespace)]
#else
    [XmlRoot(ElementName = "Entry", Namespace = CofeConstants.Namespace)]
#endif
    public class CofeSystemInfo : ICofeSystemInfo, IXmlSerializable, ISerializable
    {
        #region Constructor

        public CofeSystemInfo(SerializationInfo info, StreamingContext context)
        {
        }

        public CofeSystemInfo(IPropertyHost properties, IDynamicPropertyInterfaceProvider[] providers)
        {
            Properties = properties;
            InterfaceProviders = providers;
        }

        public CofeSystemInfo(IPropertyHost properties)
            : this(properties,
            CofeServices.ServiceLocater.FindAllServices<IDynamicPropertyInterfaceProvider>().ToArray())
        {

        }

        public CofeSystemInfo()
            : this(CofeServices.EntryConstructor.ConstructPropertyHost())
        {

        }


        #endregion

        #region Methods

        public async Task DeleteAsync()
        {
            if (Properties.Behaviors.IsPropertySupported(CofeProperties.DeleteAsync))
                await Properties.Behaviors.InvokeAsync(CofeProperties.DeleteAsync);
            else throw new NotSupportedException("Delete is not supported.");
        }

        public async Task RefreshAsync()
        {
            await Properties.Behaviors.InvokeAsync(CofeProperties.RefreshAsync);
        }


        public T As<T>() where T : IDynamicInterface<IPropertyHost>
        {
            if (this is T)
                return (T)(object)this;

            object cache;
            lock (_caches) cache = _caches.FirstOrDefault(e => e is T);
            if (cache != null) return (T)cache;

            if (Is<T>())
            {
                foreach (var p in InterfaceProviders)
                {
                    if (p.CanCastAs<T>(this.Entity))
                    {
                        var retVal = p.CastAs<T>(this.Entity);
                        (retVal as CofeSystemInfo)._caches = _caches;
                        lock (_caches)
                            _caches.Add(retVal);
                        return retVal;
                    }
                }
            }

            return default(T);
        }

        public bool Is<T>() where T : IDynamicInterface<IPropertyHost>
        {
            if (this is T)
                return true;

            lock (_caches)
                if (_caches.Any(e => e is T))
                    return true;

            foreach (var p in InterfaceProviders)
            {
                if (p.CanCastAs<T>(this.Entity))
                    return true;
            }

            return false;
        }

        public override string ToString()
        {
            if (Properties.State == PropertyHostState.Default)
                return String.Format("{0} {1} - {2}", this.Type, this.ParseName, this.Id);
            return base.ToString();
        }

        public override int GetHashCode()
        {
            if (this.Properties.State == PropertyHostState.ORMtoCache)
                return new Random().Next();

            return (this.ParseName + this.Type).GetHashCode();

        }

        public override bool Equals(object obj)
        {
            if (obj is ICofeSystemInfo)
            {
                return this.GetHashCode() == obj.GetHashCode();
            }
            return false;
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            throw new NotImplementedException();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            CofeServices.EntryXmlSerializer.WriteXml(this, writer, CofeSystemInfo.FullSerialize);
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            CofeServices.EntryXmlSerializer.WriteObjectData(this, info, context, CofeSystemInfo.FullSerialize);
        }

        #endregion

        #region Data

        private static ThreadLocal<bool> _fullSerialize = new ThreadLocal<bool>(() => true);
        private List<IDynamicInterface<IPropertyHost>> _caches = new List<IDynamicInterface<IPropertyHost>>();

        #endregion

        #region Public Properties

        public static bool FullSerialize { get { return _fullSerialize.Value; } set { _fullSerialize.Value = value; } }
        public IPropertyHost Properties { get; set; }
        public dynamic DynamicProperties { get { return Properties as dynamic; } }
        public IPropertyHost Entity { get { return Properties; } }
        public IDynamicPropertyInterfaceProvider[] InterfaceProviders { get; private set; }
        public Cofe.Data.DatabaseEntry DatabaseEntry { get; protected set; }

        [DataMember]
        public Guid Id
        {
            get { return Properties.Behaviors.TryGetProperty<Guid>(CacheProperties.Id); }
            set { Properties.Behaviors.SetProperty(CacheProperties.Id, value); }
        }

        public string CachedProperties
        {
            get
            {
                return String.Join(",",
                    (from prop in Properties.SupportedDefinitions.GetSupportedProperties()
                     //Properties.Behaviors.CofeConstantsobject[]>(CacheProperties.SupportedProperties)
                     select (int)prop).ToArray());
            }
            //protected set
            //{
            //    object[] props = (from pStr in value.Split(',') select (object)Int32.Parse(pStr)).ToArray();
            //    Properties.PropertyCache.SetProperty(CacheProperties.SupportedProperties, props);
            //}
        }

        public CofeAttributes CofeAttributes
        {
            get { return Properties.Behaviors.TryGetProperty<CofeAttributes>(CofeProperties.CofeAttributes); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.CofeAttributes, value); }
        }

        //public Guid? ParentId { get;  protected set; }
        //private CofeDirectoryInfo? _parent = null;
        //public virtual CofeDirectoryInfo Parent
        //{
        //    get
        //    {
        //        return _parent.HasValue ? _parent.Value :
        //            Properties.Behaviors.TryGetProperty(CofeProperties.Parent).ValueAs<CofeDirectoryInfo>();
        //    }
        //    protected set { _parent = value; }
        //}

        ICofeDirectoryInfo ICofeSystemInfo.Parent
        {
            get
            {
                return Properties.Behaviors.TryGetProperty<ICofeDirectoryInfo>(CofeProperties.Parent);
            }
        }

        [DataMember]
        public string ParseName
        {
            get { return Properties.Behaviors.TryGetProperty<string>(CofeProperties.FullParseName); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.FullParseName, value); }
        }

        [IgnoreDataMember]
        public string Name { get { return Properties.Behaviors.TryGetProperty<string>(CofeProperties.Name); } }

        [DataMember]
        public string Label
        {
            get { return Properties.Behaviors.TryGetProperty<string>(CofeProperties.Label); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.Label, value); }
        }

        [DataMember]
        public virtual string Type
        {
            get { return Properties.Behaviors.TryGetProperty<string>(CofeProperties.Type); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.Type, value); }
        }

        [DataMember(Name = "Time")]
        public DateTime LastWriteTimeUtc
        {
            get { return Properties.Behaviors.TryGetProperty<DateTime>(CofeProperties.LastWriteTimeUtc); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.LastWriteTimeUtc, value); }
        }

        [IgnoreDataMember]
        public DateTime CreationTimeUtc
        {
            get { return Properties.Behaviors.TryGetProperty<DateTime>(CofeProperties.CreationTimeUtc); }
            protected set { Properties.Behaviors.SetProperty(CofeProperties.CreationTimeUtc, value); }
        }

        [IgnoreDataMember]
        public IEntryTypeInfo EntryTypeInfo
        {
            get
            {
                return (this is IEntryTypeInfo) ? null :
                  Properties.Behaviors.TryGetProperty<EntryTypeInfo>(CofeEntryTypeProperties.EntryTypeInfo);
            }
            set
            {
                if (Properties.State == PropertyHostState.ORMtoCache)
                    Properties.Behaviors.SetProperty(CofeEntryTypeProperties.EntryTypeInfo, value);
            }
        }

        //public string EntryTypeKey
        //{
        //    get
        //    {
        //        return (this is IEntryTypeInfo) ? null :
        //            Properties.Behaviors.TryGetProperty(CofeEntryTypeProperties.EntryTypeKey).ValueAs<string>();
        //    }
        //    protected set { Properties.Behaviors.SetProperty(CofeEntryTypeProperties.EntryTypeKey, value); }
        //}

        public int Position
        {
            get { return Properties.Behaviors.TryGetProperty<Int32>(CofeProperties.Position); }
            set { Properties.Behaviors.SetPropertyAsync(PropertyPair.FromValue(CofeProperties.Position, value)).Wait(); }
        }

        [DataMember]
        [NotMapped]
        public XmlLinkList Links
        {
            get { return new XmlLinkList(Properties.Behaviors.Invoke<IEnumerable<XmlLink>>(CofeProperties.ListLinks)); }
            set { }
        }

        #endregion







    }
}
